# -*- coding: utf-8 -*-

""" 
pip3 install grpcio-tools
pip3 install protobuf
pip3 install grpcio
python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. message_proto.proto
"""
import nest_asyncio
nest_asyncio.apply()
import time
import grpc
import threading
import json
import asyncio
import zmq
from concurrent import futures

from framework_strategy.gopy import settings
from framework_strategy.gopy.gateways.proto import message_proto_pb2, message_proto_pb2_grpc
from framework_strategy.gopy.gateways.hprose import HproseClient
from grpc.experimental import aio
from google.protobuf.json_format import MessageToJson, MessageToDict


count = 0


class EchoProtoServerProxy(message_proto_pb2_grpc.EchoProtoServerServicer, HproseClient):

    def request(self, request, context):
        global count
        try:

            n = time.time()

            response = message_proto_pb2.GateWayResponse()
            method = request.method
            if not hasattr(self, method):
                response.data = "This method is not available, please query the supported method using describe"
                return response

            func = getattr(self, method)
            payload = json.loads(request.data) if request.data != "" else None
            # res = func(json.loads(request.data)) if request.data else func()
            future = asyncio.run_coroutine_threadsafe(
                func(payload) if payload else func(), self.loop)

            # res = {"data":"3213213"}
            # print("=====================================", count, time.time() - n)
            # count += 1
            # response.data = json.dumps(res)

            if payload and payload.pop("wait", True):
                response.data = json.dumps(future.result())
            else:
                response.data = json.dumps({"code": 0, "message": "sucess"})
            return response
        except Exception as e:

            error = "RPC Service exception: %s", e
            response = message_proto_pb2.GateWayResponse()
            response.data = error
            return response

    def start_server(self): ...


class RPCAdapter:
    """ """

    def __init__(self, main_engine, event_engine, gateway_name: str = None, proxy=None) -> None:
        """ """
        self.gateway_name = gateway_name
        self.main_engine = main_engine
        self.event_engine = event_engine
        self.client = main_engine.client
        self.loop = asyncio.get_event_loop()

        self._thread = threading.Thread(
            target=self._start, daemon=True, name="RPCAdapter")

    async def server(self):
        """
        Thread Pool is used to process the server's tasks concurrently
        Defines the server and sets the maximum number of connections. 
        Concurrent. futures is a concurrent library, 
        similar to a thread pool concept

        Don't use SSL

        his method is only safe to call before the server is started.

        """
        grpc_server = aio.server(
            futures.ThreadPoolExecutor(max_workers=settings.PRC_MAX_WORK))
        grpc_server.add_insecure_port(
            settings.RPC_HOST + ':' + str(settings.RPC_PORT))
        message_proto_pb2_grpc.add_EchoProtoServerServicer_to_server(EchoProtoServerProxy(
            self.main_engine,
            self.event_engine,
            self.gateway_name,
            loop=self.loop,
        ), grpc_server)

        # This method can only be called once, and start() does not block
        await grpc_server.start()
        print("server start...")
        await grpc_server.wait_for_termination()

    def _start(self):
        self.loop.run_until_complete(asyncio.wait([self.server()]))

    def start(self):
        """ """
        self._thread.start()

    def close(self):
        """ """
        self.loop.close()


class ZMQAdapter(HproseClient):
    """ 
    pip3 install pyzmq
    apt-get install libzmq3-dev
    ---
    https://www.cnblogs.com/hust2012/archive/2012/12/26/2834666.html
    yum install gcc
    yum install gcc-c++
    yum install make
    yum install uuid-devel
    yum install libuuid-devel
    """

    def __init__(self, main_engine, event_engine, gateway_name: str = None, proxy=None) -> None:

        self.gateway_name = gateway_name
        self.main_engine = main_engine
        self.event_engine = event_engine
        self.client = main_engine.client
        self._thread = threading.Thread(
            target=self._start, daemon=True, name="ZMQAdapter")
        self.loop = asyncio.get_event_loop()

        context = zmq.Context()
        self.socket = context.socket(zmq.PULL)
        self.socket.bind(
            "tcp://{}:{}".format(settings.ZMQ_HOST, settings.ZMQ_PORT))

    def _start(self):
        while True:
            message = self.socket.recv_string()
            self.request(message)

    def request(self, message):
        """ """
        n = time.time()
        try:

            data = json.loads(message)
            method = data["method"]
            param = data["param"]

            func = getattr(self, method)
            payload = json.loads(param) if param and param != "" else None
            future = asyncio.run_coroutine_threadsafe(
                func(json.loads(payload)) if payload else func(), self.loop)

            timeout = int(data.pop("timeout", 0)) if isinstance(
                data, dict) else None
            if not timeout or timeout < 1:
                data = json.dumps({"code": 0, "message": "sucess"})
            else:
                data = json.dumps(future.result())
            print("dddddddd", time.time() - n)
            return data
        except Exception as e:
            error = "RPC Service exception: %s", e
            data = error
            return data

    def start(self):
        """ """
        self._thread.start()

    def close(self):
        """ """
        self.loop.close()


class RPCManager:
    """ """

    exchanges = ["RPCManagerClient"]

    def __init__(self, main_engine, event_engine, gateway_name: str = None, proxy=None) -> None:
        """ """
        self.gateway_name = gateway_name or "rpc-manager"
        self.rpc_server = RPCAdapter(main_engine, event_engine, gateway_name)
        self.rpc_server.start()
        print("[RPCManagerClient] RPCManager init success")

    def close(self):
        """ """
        pass
